using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class StatementsFeature {
        Production BuildStatementProduction (string itemName) {
            var endBlockStatementDelimiter = new EndBlockStatementDelimiterFactory ().Create ();
            var choice = new ChoiceProduction (
                BeginBlockProduction.CreateTerminal (),
                endBlockStatementDelimiter.EndBlock,
                endBlockStatementDelimiter.StatementDelimiter,
                IdentifierProduction.CreateTerminal ()
                );

            return new NonTerminal (
                "statements",
                new MultipleRule (0, -1, new NamedRule (itemName, choice)),
                new [] {itemName}
                );
        }

        [Test]
        public void ShouldParseIndentedBlock () {
            AssertTokens (new [] { "one", "{", "two", ";", "three", "}", ";", "four" },
@"one
    two
    three
four");
        }

        [Test]
        public void ShouldParseBracedBlockWithoutNewLine () {
            AssertTokens (new [] { "one", "{", "two", ";", "three", ";", "}", "four" },
@"one {
    two
    three
} four");
        }

        [Test]
        public void ShouldParseBracedBlock () {
            AssertTokens (new [] { "one", "{", "two", ";", "three", ";", "}", ";", "four" },
@"one {
    two
    three
}
four");
        }

        private void AssertTokens (string [] tokens, string s) {
            string[] actualTokens = ConvertTokensToStrings (ParseToTerms (s));
            Assert.AreEqual (tokens, actualTokens);
        }

        private string [] ConvertTokensToStrings (ListTerm tokens) {
            return tokens.Terms.Select (t => ConvertTokenToString (t)).ToArray ();
        }

        private string ConvertTokenToString (ITerm term) {
            if (term is IdentifierTerm) {
                return ((IdentifierTerm) term).Name;
            } else if (term is KeywordTerm) {
                return ((KeywordTerm) term).Name;
            } else {
                Assert.Fail("expected keyword or identifier" + term);
                return null;
            }
        }

        private ListTerm ParseToTerms (string s) {
            var production = BuildStatementProduction ("tokens");

            var term = production.Parse (s, 0, new ParseContext ());

            if (term != null) {
                var compositeTerm = term.Term as CompositeTerm;

                return compositeTerm.SubTerms["tokens"] as ListTerm;
            } else {
                return null;
            }
        }
    }
}